package pay.utils;

import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pay.serviceImpl.OrderHelperImpl;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import org.apache.commons.lang3.StringUtils;

/**
 * 
 * Title:RedisCilent Description: Redis 緩存類說明 Company:HK
 * 
 * @author Sam
 * @date 2016年2月24日下午2:12:24
 */
public class RedisCilent {
	
	private static Logger logger = LoggerFactory.getLogger(RedisCilent.class);
	
	// jedis池
	private static JedisPool pool;
	// shardedJedis池
	private static ShardedJedisPool shardPool;
	// 静态代码初始化池配置
	static {
		// 加载redis配置文件
		ResourceBundle bundle = ResourceBundle.getBundle("redis");
		String Env = "redis-"+System.getProperty("Env");
        logger.info("Env :"+Env);

		ResourceBundle envBundle = null;

		if(StringUtils.isNoneEmpty(Env))
			envBundle = ResourceBundle.getBundle(Env);

		if(envBundle != null){
			bundle = envBundle;
		}

		if (bundle == null) {
			throw new IllegalArgumentException(
					"[redis.properties] is not found!");
		}
		// 创建jedis池配置实例
		JedisPoolConfig config = new JedisPoolConfig();

		int maxActivity = Integer.valueOf(bundle
				.getString("redis.pool.maxActive"));
		int maxIdle = Integer.valueOf(bundle.getString("redis.pool.maxIdle"));
		long maxWait = Long.valueOf(bundle.getString("redis.pool.maxWait"));

		// 设置最大连接数
		config.setMaxTotal(maxActivity);
		// 设置最大空闲数
		config.setMaxIdle(maxIdle);
		// 设置超时时间
		config.setMaxWaitMillis(maxWait);
		
		logger.info("************************************************************");
		logger.info("RedisCilent setting maxActivity={}, maxIdle={}, maxWait={}", maxActivity, maxIdle, maxWait);
		logger.info("************************************************************");
		// 根据配置实例化jedis池
		pool = new JedisPool(config, bundle.getString("redis.ip"),
				Integer.valueOf(bundle.getString("redis.port")));

		// 创建多个redis共享服务
		JedisShardInfo jedisShardInfo1 = new JedisShardInfo(
				bundle.getString("redis1.ip"), Integer.valueOf(bundle
						.getString("redis.port")));

		JedisShardInfo jedisShardInfo2 = new JedisShardInfo(
				bundle.getString("redis2.ip"), Integer.valueOf(bundle
						.getString("redis.port")));

		List<JedisShardInfo> list = new LinkedList<JedisShardInfo>();
		list.add(jedisShardInfo1);
		list.add(jedisShardInfo2);

		// 根据配置文件,创建shared池实例
		shardPool = new ShardedJedisPool(config, list);
	}

	/**
	 * 执行器,的辅助类， 它保证在执行操作之后释放数据源returnResource(jedis)
	 * 
	 * @version V1.0
	 * @param <T>
	 */
	abstract static class Executor<T> {

		ShardedJedis jedis;

		ShardedJedisPool shardedJedisPool;

		public Executor(ShardedJedisPool shardedJedisPool) {
			this.shardedJedisPool = shardedJedisPool;
			jedis = this.shardedJedisPool.getResource();
		}

		/**
		 * 回调
		 * 
		 * @return 执行结果
		 */
		abstract T execute();

		/**
		 * 调用{@link #execute()}并返回执行结果 它保证在执行{@link #execute()}
		 * 之后释放数据源returnResource(jedis)
		 * 
		 * @return 执行结果
		 */
		public T getResult() {
			T result = null;
			try {
				result = execute();
			} catch (Exception e) {
				throw new RuntimeException("Redis execute exception", e);
			} finally {
				if (jedis != null) {
					shardedJedisPool.returnResource(jedis);
				}
			}
			return result;
		}
	}

	/**
	 * 向缓存中设置字符串内容
	 * 
	 * @param key
	 *            key
	 * @param value
	 *            value
	 * @return
	 * @throws Exception
	 */
	public static Boolean setString(final String key, final String value) {
		return new Executor<Boolean>(shardPool) {
			@Override
			Boolean execute() {
				jedis.set(key, value);
				return true;
			}

		}.getResult();
	}

	/**
	 * 将值 value 关联到 key ，并将 key 的生存时间设为 expire (以秒为单位)。 如果 key 已经存在， 将覆写旧值。
	 * 类似于以下两个命令: SET key value EXPIRE key expire # 设置生存时间
	 * 不同之处是这个方法是一个原子性(atomic)操作，关联值和设置生存时间两个动作会在同一时间内完成，在 Redis 用作缓存时，非常实用。
	 * 时间复杂度：O(1)
	 * 
	 * @param key
	 *            key
	 * @param value
	 *            string value
	 * @param expire
	 *            生命周期
	 * @return 设置成功时返回 OK 。当 expire 参数不合法时，返回一个错误。
	 */
	public static String setString(final String key, final String value,
			final int expire) {
		return new Executor<String>(shardPool) {
			@Override
			String execute() {
				return jedis.setex(key, expire, value);
			}
		}.getResult();
	}

	/**
	 * 返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 nil 。 假如 key 储存的值不是字符串类型，返回一个错误，因为
	 * getString 只能用于处理字符串值。 时间复杂度: O(1)
	 * 
	 * @param key
	 *            key
	 * @return 当 key 不存在时，返回 nil ，否则，返回 key 的值。如果 key 不是字符串类型，那么返回一个错误。
	 */
	public static String getString(final String key) {
		return new Executor<String>(shardPool) {

			@Override
			String execute() {
				return jedis.get(key);
			}
		}.getResult();
	}

	/**
	 * 查询是否存在这个[String] ＫＥＹ
	 * 
	 * @param key
	 * @return
	 */
	public static Boolean existsKey(final String key) {
		return new Executor<Boolean>(shardPool) {
			@Override
			Boolean execute() {
				return jedis.exists(key);
			}
		}.getResult();
	}

	/**
	 * 
	 * @param key
	 * @return byte[]
	 */
	public static Boolean existsKey(final byte[] key) {
		return new Executor<Boolean>(shardPool) {
			@Override
			Boolean execute() {
				return jedis.exists(key);
			}
		}.getResult();
	}

	public static String setByte(final String key, final byte[] value) {
		return new Executor<String>(shardPool) {
			@Override
			String execute() {
				jedis.set(key.getBytes(), value);
				return key;
			}
		}.getResult();
	}

	public static byte[] getByte(final String key) {
		return new Executor<byte[]>(shardPool) {
			@Override
			byte[] execute() {
				return jedis.get(key.getBytes());
			}
		}.getResult();
	}

	public static Long hashSet(final String key, final String filed,
			final String value) {
		return new Executor<Long>(shardPool) {
			@Override
			Long execute() {
				return jedis.hset(key, filed, value);
			}
		}.getResult();
	}

	public static Long listSet(final String key, final String value) {
		return new Executor<Long>(shardPool) {
			@Override
			Long execute() {
				return jedis.lpush(key, value);
			}
		}.getResult();
	}

	public static List<String> listGet(final String key, final long start,
			final long end) {
		return new Executor<List<String>>(shardPool) {
			@Override
			List<String> execute() {
				return jedis.lrange(key, start, end);
			}
		}.getResult();
	}

	public static String hashGet(final String key, final String filed) {
		return new Executor<String>(shardPool) {
			@Override
			String execute() {
				return jedis.hget(key, filed);
			}
		}.getResult();
	}

	public static Long delKey(final String key) {
		return new Executor<Long>(shardPool) {
			@Override
			Long execute() {
				return jedis.del(key);
			}
		}.getResult();
	}

	/**
	 * 
	 * @param key
	 *            需要删除的List key
	 * @param count
	 *            移除等于value的元素，当count>0时，从表头开始查找，移除count个；当count=0时，从表头开始查找，
	 *            移除所有等于value的；当count<0时，从表尾开始查找，移除|count| 个
	 * @param value
	 *            删除的value
	 * @return
	 */
	public static Long delListVal(final String key, int count, String value) {
		return new Executor<Long>(shardPool) {
			@Override
			Long execute() {
				return jedis.lrem(key, count, value);
			}
		}.getResult();
	}

}
